package com.lagou.edu.factory;

import com.alibaba.druid.util.StringUtils;
import com.lagou.edu.annotation.*;
import org.reflections.Reflections;

import java.beans.Introspector;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Ma
 * @date 2021/12/28 9:46
 */
public class BeanFactory {

    private static final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(64);  // 存储对象

    static {
        try {
            //扫描包，通过反射技术实例化对象并且存储待用（map集合）
            Reflections eduPackage = new Reflections("com.lagou.edu");
            Set<Class<?>> componentClass = eduPackage.getTypesAnnotatedWith(Component.class);
            //遍历对象集合
            for (Class<?> clazz : componentClass) {
                if (clazz.isAnnotation()) {
                    continue;
                }
                // 获取实例化对象
                initialSingletonObjects(clazz);
            }
            //维护对象之间依赖关系和解决事务代理问题
            resolveDependencyAndTransaction();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void resolveDependencyAndTransaction() throws IllegalAccessException {
        for (Map.Entry<String, Object> entrySet : singletonObjects.entrySet()) {
            Object obj = entrySet.getValue();
            Class<?> clazz = obj.getClass();
            resolveDependency(obj, clazz);
            resolveTransaction(entrySet, obj, clazz);
        }
    }

    private static void initialSingletonObjects(Class<?> clazz) throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        Object object = clazz.getDeclaredConstructor().newInstance();
        Component component = clazz.getAnnotation(Component.class);
        Service service = clazz.getAnnotation(Service.class);
        Repository repository = clazz.getAnnotation(Repository.class);
        if (component != null) {
            if (StringUtils.isEmpty(component.value())) {
                String simpleName = clazz.getSimpleName();
                singletonObjects.put(Introspector.decapitalize(simpleName), object);
            } else {
                singletonObjects.put(component.value(), object);
            }
        } else if (service != null) {
            if (StringUtils.isEmpty(service.value())) {
                String simpleName = clazz.getSimpleName();
                singletonObjects.put(Introspector.decapitalize(simpleName), object);
            } else {
                singletonObjects.put(service.value(), object);
            }
        } else if (repository != null) {
            //判断Repository注解上是否有自定义对象ID
            if (StringUtils.isEmpty(repository.value())) {
                String simpleName = clazz.getSimpleName();
                singletonObjects.put(Introspector.decapitalize(simpleName), object);
            } else {
                singletonObjects.put(repository.value(), object);
            }
        }
    }

    private static void resolveDependency(Object obj, Class<?> clazz) throws IllegalAccessException {
        //获取每个类的所有属性
        Field[] fields = clazz.getDeclaredFields();
        //遍历属性，确认是否有使用Autowired注解，有使用注解则需要完成注入
        for (Field field : fields) {
            //判断是否使用注解的参数
            if (field.isAnnotationPresent(Autowired.class)
                    && field.getAnnotation(Autowired.class).required()) {
                field.setAccessible(true);
                field.set(obj,singletonObjects.get(field.getName()));
            }
        }
    }

    private static void resolveTransaction(Map.Entry<String, Object> entrySet, Object obj, Class<?> clazz) {
        if (clazz.getAnnotation(Transactional.class)!=null) {
            //获取代理工厂
            //判断当前类是否有Transactional注解，若有则使用代理对象
            ProxyFactory proxyFactory = (ProxyFactory) BeanFactory.getBean("proxyFactory");
            Class<?>[] face = clazz.getInterfaces();//获取类c实现的所有接口
            //判断对象是否实现接口
            if (face.length > 0) {
                //实现使用JDK
                obj = proxyFactory.getJdkProxy(obj);
            } else {
                //没实现使用CGLIB
                obj = proxyFactory.getCglibProxy(obj);
            }
        }
        // 把处理之后的object重新放到map中
        singletonObjects.put(entrySet.getKey(), obj);
    }

    public static Object getBean(String id) {
        return singletonObjects.get(id);
    }

}
